package com.haibara.codesubmiteval.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.haibara.codesubmiteval.common.ErrorCode;
import com.haibara.codesubmiteval.constant.CommonConstant;
import com.haibara.codesubmiteval.constant.RedisKeyConstant;
import com.haibara.codesubmiteval.context.UserContext;
import com.haibara.codesubmiteval.exception.ThrowUtils;
import com.haibara.codesubmiteval.kafka.processor.Processor;
import com.haibara.codesubmiteval.mapper.QuestionMapper;
import com.haibara.codesubmiteval.mapper.QuestionSubmitMapper;
import com.haibara.codesubmiteval.model.dto.questionSubmit.QuestionSubmitAddRequest;
import com.haibara.codesubmiteval.model.dto.questionSubmit.QuestionSubmitQueryRequest;
import com.haibara.codesubmiteval.model.dto.questionSubmit.QuestionSubmitUpdateRequest;
import com.haibara.codesubmiteval.model.dto.questionSubmit.QuestionSubmitVOQueryRequest;
import com.haibara.codesubmiteval.model.entity.QuestionCase;
import com.haibara.codesubmiteval.model.entity.QuestionSubmit;
import com.haibara.codesubmiteval.model.enums.JudgeResultEnum;
import com.haibara.codesubmiteval.model.enums.QuestionSubmitStatusEnum;
import com.haibara.codesubmiteval.model.vo.Least5TimeSubmitVO;
import com.haibara.codesubmiteval.model.vo.QuestionSubmitVO;
import com.haibara.codesubmiteval.service.QuestionCaseService;
import com.haibara.codesubmiteval.service.QuestionSubmitService;
import com.haibara.codesubmiteval.utils.RedisRecoveryUtil;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;


/**
 * @Author: mengyu
 * @Date: 2025/2/6
 */
@Service
public class QuestionSubmitServiceImpl extends ServiceImpl<QuestionSubmitMapper, QuestionSubmit> implements QuestionSubmitService {


    @Resource
    private QuestionMapper questionMapper;
    @Resource
    private QuestionSubmitMapper questionSubmitMapper;
    @Resource
    private Processor processor;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private QuestionCaseService questionCaseService;

    @Override
    @Transactional
    public Long doSubmit(QuestionSubmitAddRequest questionSubmitAddRequest) {
        // 先判断用户有没有正在判题中的请求
        LambdaQueryWrapper<QuestionSubmit> lqw = new LambdaQueryWrapper<>();
        lqw.eq(true, QuestionSubmit::getJudgeResult, JudgeResultEnum.WAITING.getText())
                .eq(true, QuestionSubmit::getUserId, UserContext.get().getId());
        // 2. 判断题目是否有测试用例
        List<QuestionCase> questionCases = questionCaseService.getQuestionCase(questionSubmitAddRequest.getQuestionId());
        ThrowUtils.throwIf(questionCases == null || questionCases.isEmpty(), ErrorCode.OPERATION_ERROR, "该题目未设置测试用例，请联系管理员！");
        boolean exists = this.getBaseMapper().exists(lqw);
        log.error(String.valueOf(exists));
        ThrowUtils.throwIf(exists, ErrorCode.OPERATION_ERROR, "您有正在判题的题目");
        QuestionSubmit questionSubmit = new QuestionSubmit();
        BeanUtil.copyProperties(questionSubmitAddRequest, questionSubmit);
        questionSubmit.setQuestionTitle(questionMapper.selectById(questionSubmitAddRequest.getQuestionId()).getTitle());
        questionSubmit.setUserId(UserContext.get().getId());
        questionSubmit.setStatus(QuestionSubmitStatusEnum.WAITING.getValue());
        questionSubmit.setJudgeResult(JudgeResultEnum.WAITING.getText());
        int insert = this.baseMapper.insert(questionSubmit);
        ThrowUtils.throwIf(insert != 1, ErrorCode.SYSTEM_ERROR, "新增题目提交记录失败");
        // 记录至Redis，标记该题目正在判题
        String key = RedisKeyConstant.getCompleteKey(RedisKeyConstant.executingCodeId, questionSubmit.getId().toString());
        stringRedisTemplate.opsForValue().set(key, "");
        // 使该题目提交数+1
        int i = questionMapper.increaseQuestionSubmitNum(questionSubmit.getQuestionId());
        ThrowUtils.throwIf(i != 1, ErrorCode.SYSTEM_ERROR, "新增题目提交数失败 : " + questionSubmit.getQuestionId());
        // 获取该提交记录id，交由JudgeService进行判题
        processor.sendMsg(questionSubmit.getId());
        // 题目提交数+1
        stringRedisTemplate.opsForValue().increment(RedisKeyConstant.totalQuestionSubmitNumber, 1L);
        return questionSubmit.getId();
    }

    /**
     * 根据id查找题目提交记录VO信息
     *
     * @param id 题目提交记录id
     * @return VO信息
     */
    @Override
    public QuestionSubmitVO getQuestionSubmitVOById(Long id) {
        // 先判断是否在查询
        // 从Redis中判断
        Boolean isExecuting = stringRedisTemplate.hasKey(RedisKeyConstant.getCompleteKey(RedisKeyConstant.executingCodeId, id.toString()));
        if (Boolean.TRUE.equals(isExecuting)) {
            // 代表正在执行代码
            QuestionSubmitVO questionSubmitVO = new QuestionSubmitVO();
            questionSubmitVO.setJudgeResult(JudgeResultEnum.WAITING.getText());
            return questionSubmitVO;
        }
        QuestionSubmit questionSubmit = this.baseMapper.selectById(id);
        ThrowUtils.throwIf(questionSubmit == null, ErrorCode.NOT_FOUND_ERROR, "题目提交记录不存在 : " + id);
        if (questionSubmit.getJudgeResult().equals(JudgeResultEnum.WAITING.getText())) {
            // 还在运行状态
            // 记录至Redis，标记该题目正在判题
            String key = RedisKeyConstant.getCompleteKey(RedisKeyConstant.executingCodeId, id.toString());
            stringRedisTemplate.opsForValue().set(key, "");
        }
        return QuestionSubmitVO.getQuestionSubmitVOByQuestionSubmit(questionSubmit);
    }

    @Override
    public String getHistoryCode(Long userId, Long questionId) {
        return questionSubmitMapper.getHistoryCode(questionId, userId);
    }

    @Override
    public List<Least5TimeSubmitVO> getLeast5TimesSubmit(Long userId) {
        LambdaQueryWrapper<QuestionSubmit> lqw = new LambdaQueryWrapper<>();
        lqw.eq(userId != null, QuestionSubmit::getUserId, userId)
                .ne(true, QuestionSubmit::getJudgeResult, JudgeResultEnum.WAITING.getText())
                .orderBy(true, false, QuestionSubmit::getCreateTime);
        Page<QuestionSubmit> questionSubmitPage = this.page(new Page<>(0, 5), lqw);
        return questionSubmitPage.getRecords().stream().map(Least5TimeSubmitVO::getLeast5TimeSubmitVOByQuestionSubmit).toList();
    }

    @Override
    public void updateQuestionSubmit(QuestionSubmitUpdateRequest questionSubmitUpdateRequest) {
        QuestionSubmit questionSubmit = new QuestionSubmit();
        BeanUtil.copyProperties(questionSubmitUpdateRequest, questionSubmit);
        int i = this.getBaseMapper().updateById(questionSubmit);
        ThrowUtils.throwIf(i != 1, ErrorCode.SYSTEM_ERROR, "更新提交记录失败");
    }

    @Override
    public Page<QuestionSubmit> pageQuestionSubmit(QuestionSubmitQueryRequest questionSubmitQueryRequest) {
        Long id = questionSubmitQueryRequest.getId();
        String questionTitle = questionSubmitQueryRequest.getQuestionTitle();
        Long userId = questionSubmitQueryRequest.getUserId();
        String judgeResult = questionSubmitQueryRequest.getJudgeResult();
        Integer status = questionSubmitQueryRequest.getStatus();
        long current = questionSubmitQueryRequest.getCurrent();
        long pageSize = questionSubmitQueryRequest.getPageSize();
        String sortField = questionSubmitQueryRequest.getSortField();
        String sortOrder = questionSubmitQueryRequest.getSortOrder();

        LambdaQueryWrapper<QuestionSubmit> lqw = new LambdaQueryWrapper<>();
        lqw.like(!StrUtil.isBlankIfStr(questionTitle), QuestionSubmit::getQuestionTitle, questionTitle)
                .eq(id != null, QuestionSubmit::getId, id)
                .eq(userId != null, QuestionSubmit::getUserId, userId)
                .eq(status != null, QuestionSubmit::getStatus, status)
                .eq(!StrUtil.isBlankIfStr(judgeResult), QuestionSubmit::getJudgeResult, judgeResult);
        // 排序
        if (!StrUtil.isBlankIfStr(sortField)) {
            // 根据创建时间排序
            lqw.orderBy("id".equals(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC), QuestionSubmit::getId);
            lqw.orderBy("createTime".equals(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC), QuestionSubmit::getCreateTime);
            lqw.orderBy("updateTime".equals(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC), QuestionSubmit::getUpdateTime);
        } else {
            // 默认以创建时间倒序排列
            lqw.orderBy(true, false, QuestionSubmit::getCreateTime);
        }
        // 获取QuestionSubmit分页
        return this.page(new Page<>(current, pageSize), lqw);
    }

    @Override
    public Page<QuestionSubmitVO> pageQuestionSubmitVO(QuestionSubmitVOQueryRequest questionSubmitVOQueryRequest) {
        QuestionSubmitQueryRequest questionSubmitQueryRequest = new QuestionSubmitQueryRequest();
        BeanUtil.copyProperties(questionSubmitVOQueryRequest, questionSubmitQueryRequest);
        // 获取QuestionSubmit分页
        Page<QuestionSubmit> questionSubmitPage = this.pageQuestionSubmit(questionSubmitQueryRequest);
        // 转换
        Page<QuestionSubmitVO> result = new Page<>();
        BeanUtil.copyProperties(questionSubmitPage, result);
        // entity -> VO
        result.setRecords(
                questionSubmitPage.getRecords().stream().map(
                        QuestionSubmitVO::getQuestionSubmitVOByQuestionSubmit
                ).toList()
        );
        return result;
    }
}
